﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Mads.CoreDomain;
using Mads.CoreDomain.Enumerations;
using System.ServiceModel;
using MySql.Data;
using MySql.Data.MySqlClient;


namespace Mads.WcfService
{
  //TODO:: make singletone!!!
  [ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)]

  class TrainManager : ITrainManager
  {
    private TrainBaseConnector trainBaseConnector;

    public TrainManager()
    {
      trainBaseConnector = TrainBaseConnector.Instance;
    }

    private Dictionary<int, ClientUser> users = new Dictionary<int, ClientUser>();

    public int InitUser()
    {
      int newId = 0;
      if (users.Keys.Count > 0)
        newId = users.Keys.Max() + 1;
      ClientUser user = new ClientUser(trainBaseConnector);
      users.Add(newId, user);
      return newId;
    }

    public bool BuyTicket(int userId, FreeTicket ticket, Station departure,
      Station arrival, string ownerName, string ownerDocNumber)
    {
      if (!users.Keys.Contains(userId))
      {
        users.Add(userId, new ClientUser(trainBaseConnector));
      }
      ClientUser user = users[userId];
      return user.BuyTicket(ticket, departure, arrival, ownerName, ownerDocNumber);
    }

    public TicketToReturn GetTicketByNumber(int userID, string logicalID)
    {
      if (!users.Keys.Contains(userID))
      {
        users.Add(userID, new ClientUser(trainBaseConnector));
      }
      ClientUser user = users[userID];
      Ticket ticket = user.GetTicketByNumber(logicalID);
      return ticket == null
        ? null
        : new TicketToReturn(
          (uint)ticket.ID,
          ticket.Seat.Race.Route.LogicalID,
          ticket.Seat.Car.Number,
          ticket.Seat.Number);
    }

    public bool BuyPendingTicket(int userID, Ticket ticket)
    {
      if (!users.Keys.Contains(userID))
      {
        users.Add(userID, new ClientUser(trainBaseConnector));
      }
      ClientUser clientUser;
      ClientCashier clientCashier;
      if (!users.TryGetValue(userID, out clientUser) ||
         (clientCashier = clientUser as ClientCashier) == null)
      {
        return false;
      }
      return clientCashier.BuyTicket(ticket);
    }

    public IEnumerable<FreeTicket> GetTickets(
      int userId, DateTime departureTime,
      Station departureStation, Station arrivalStation)
    {
      if (!users.Keys.Contains(userId))
      {
        users.Add(userId, new ClientUser(trainBaseConnector));
      }
      ClientUser user = users[userId];
      return user.GetTickets(
        departureTime, departureTime + new TimeSpan(23, 59, 59),
        departureStation, arrivalStation);
    }

    public bool ReturnTicket(int userId, string logicalID)
    {
      if (!users.Keys.Contains(userId))
      {
        users.Add(userId, new ClientUser(trainBaseConnector));
      }
      ClientUser clientUser;
      ClientCashier clientCashier;
      if (!users.TryGetValue(userId, out clientUser) ||
         (clientCashier = clientUser as ClientCashier) == null)
      {
        return false;
      }
      Ticket ticket = clientCashier.GetTicketByNumber(logicalID); ;
      return clientCashier.ReturnTicket(ticket);
    }


    public bool Login(int userId, string name, string password, out bool mustCheck)
    {
      if (!users.Keys.Contains(userId))
      {
        users.Add(userId, new ClientUser(trainBaseConnector));
      }
      mustCheck = false;
      User user = trainBaseConnector.Login(name, password);
      if (user == null)
      {
        if (users[userId].IsBot())
          mustCheck = true;
        return false;
      }
      users[userId].LoginSucceed();
      if (user.Role == CoreDomain.Enumerations.UserRole.User)
      {
        users[userId] = new ClientUser(trainBaseConnector, user);
      }
      else if (user.Role == CoreDomain.Enumerations.UserRole.Cashier)
      {
        users[userId] = new ClientCashier(trainBaseConnector, user);
      }
      else if (user.Role == CoreDomain.Enumerations.UserRole.Administrator)
      {
        users[userId] = new ClientAdministrator(trainBaseConnector, user);
      }
      return true;
    }
    public void Logout(int userId)
    {
      if (!users.Keys.Contains(userId))
      {
        users.Add(userId, new ClientUser(trainBaseConnector));
      }
      users[userId] = new ClientUser(trainBaseConnector);
    }
    public void Shutdown(int userId)
    {
      users.Remove(userId);
    }
    public bool Register(int id, string name, string password, string email, int userRole)
    {
      UserRole role = (UserRole)userRole;
      if (!users.Keys.Contains(id))
      {
        users.Add(id, new ClientUser(trainBaseConnector));
      }
      ClientUser clientUser = users[id];
      if (clientUser == null || (!(clientUser is ClientAdministrator)
        && role != UserRole.User))
        return false;
      User user = new User(name, password, email, role);
      trainBaseConnector.Add<User>(user);
      if (user.ID == null)
        return false;
      MailSender.SendMail(name, password, email);
      return true;
    }
    public IEnumerable<Station> GetStations(int sessionId)
    {
      return trainBaseConnector.GetStations();
    }

    public bool AddRoute(int sessionId, uint routeNumber, SpeedType speedType,
      Regularity regularity, Dictionary<CarType, int> cars,
      Dictionary<TimeSpan, Station> departure, Dictionary<Station, TimeSpan> waitMinutes,
      float priceCoefficient)
    {
      if (!users.Keys.Contains(sessionId))
      {
        users.Add(sessionId, new ClientUser(trainBaseConnector));
      }
      ClientUser user = users[sessionId];
      if (!(user is ClientAdministrator))
      {
        return false;
      }
      Route route = new Route(routeNumber, speedType, regularity, 0, 0, priceCoefficient);
      trainBaseConnector.AddRoute(route);
      uint carNumber = 0;
      foreach (CarType type in cars.Keys)
      {
        for (int i = 0; i < cars[type]; i++)
        {
          carNumber++;
          trainBaseConnector.AddCar(new Car(carNumber, route, type));
        }
      }
      Func<TimeSpan, double> orderByMinutes = delegate(TimeSpan ts)
      { return ts.TotalMinutes; };
      List<TimeSpan> departureTime = departure.Keys.OrderBy(orderByMinutes).ToList();
      for (int i = 0; i < departureTime.Count; i++)
      {
        Station station = departure[departureTime.ElementAt(i)];
        if (!waitMinutes.Keys.Contains(station))
          return false;
        RouteStop stop = new RouteStop(route, station, (uint)(i + 1),
          (uint)(departureTime.ElementAt(i) - waitMinutes[station]).TotalMinutes,
          (uint)departureTime.ElementAt(i).TotalMinutes);
        trainBaseConnector.AddRouteStop(stop);
      }
      return true;
    }

    // while we can add only coefficients for all year
    public bool ChangeSeasonPrice(int sessionId, Dictionary<TimeSpan, float> seasonCoefficient)
    {
      if (!users.Keys.Contains(sessionId))
      {
        users.Add(sessionId, new ClientUser(trainBaseConnector));
      }
      ClientUser user = users[sessionId];
      if (!(user is ClientAdministrator))
      {
        return false;
      }
      TimeSpan period = new TimeSpan();
      List<SeasonPrice> price = new List<SeasonPrice>();
      foreach (TimeSpan per in seasonCoefficient.Keys)
      {
        DateTime start = new DateTime().AddYears(DateTime.Now.Year) + period;
        period += per;
        DateTime finish = new DateTime().AddYears(DateTime.Now.Year) + period;
        SeasonPrice pr = new SeasonPrice(start, finish, seasonCoefficient[per]);
        price.Add(pr);
      }
      if ((period.TotalDays != 365 && DateTime.Now.Year % 4 != 0) ||
        (period.TotalDays != 366 && DateTime.Now.Year % 4 == 0))
      {
        return false;
      }
      trainBaseConnector.ObsoleteSeasonPrice();
      foreach (SeasonPrice pr in price)
      {
        //TODO:: Add versions
        trainBaseConnector.Add<SeasonPrice>(pr);
      }
      return true;
    }
    public bool AddPrice(int sessionId, Dictionary<Dictionary<Station, Station>, float> stationPrice)
    {
      return true;
    }
    public IEnumerable<CarType> GetCarTypes(int sessionId)
    {
      return trainBaseConnector.GetCarTypes();
    }
    public string GetInfoToCheckBot(int userId)
    {
      if (!users.Keys.Contains(userId))
      {
        users.Add(userId, new ClientUser(trainBaseConnector));
      }
      ClientUser user = users[userId];
      return user.GetInfoToCheckBot();
    }
    public bool CheckBot(int userId, int sum)
    {
      if (!users.Keys.Contains(userId))
      {
        users.Add(userId, new ClientUser(trainBaseConnector));
      }
      return users[userId].CheckBot(sum);
    }
    public bool isCashier(int id)
    {
      return users.Keys.Contains(id) && (users[id] is ClientCashier);
    }

    #region Новые методы для приложения для администратора

    public IEnumerable<User> GetUsers()
    {
      IEnumerable<User> users = trainBaseConnector.GetUsers();
      return users;
    }

    public bool LoginForAdministrator(int sessionId, string name, string password, out bool isBot)
    {
      isBot = false;
      return Login(sessionId, name, password, out isBot) && (users[sessionId] is ClientAdministrator);
    }

    public bool DeleteUser(string name, string password)
    {
      return true;
    }

    public bool EditUser(string name, string password, int role)
    {
      return true;
    }

    public bool GetCars(int sessionId)
    {
      return true;
    }

    public bool AddRouteFromAdministrator(int sessionId, uint routeNumber, int speedType,
      int regularity, int CoupeCarsNumber, int SecondClassCarsNumber,
      Dictionary<TimeSpan, Station> departure, Dictionary<Station, TimeSpan> waitMinutes,
      float priceCoefficient)
    {
      if (!users.Keys.Contains(sessionId))
      {
        users.Add(sessionId, new ClientUser(trainBaseConnector));
      }
      ClientUser user = users[sessionId];
      if (!(user is ClientAdministrator))
      {
        return false;
      }
      Route route = new Route(routeNumber, (SpeedType)speedType, (Regularity)regularity, 0, 0, priceCoefficient);
      trainBaseConnector.AddRoute(route);
      uint carNumber = 0;
      /*foreach (CarType type in cars.Keys)
      {
        for (int i = 0; i < cars[type]; i++)
        {
          carNumber++;
          trainBaseConnector.AddCar(new Car(carNumber, route, type));
        }
      }*/
      for (int i = 0; i < CoupeCarsNumber; i++)
      {
        carNumber++;
        trainBaseConnector.AddCar(new Car(carNumber, route, new CarType("Купе", (float)1.5, (uint)36)));
      }
      for (int i = 0; i < CoupeCarsNumber; i++)
      {
        carNumber++;
        trainBaseConnector.AddCar(new Car(carNumber, route, new CarType("Плацкарт", (float)1, (uint)52)));
      }
      Func<TimeSpan, double> orderByMinutes = delegate(TimeSpan ts)
      { return ts.TotalMinutes; };
      List<TimeSpan> departureTime = departure.Keys.OrderBy(orderByMinutes).ToList();
      for (int i = 0; i < departureTime.Count; i++)
      {
        Station station = departure[departureTime.ElementAt(i)];
        if (!waitMinutes.Keys.Contains(station))
          return false;
        RouteStop stop = new RouteStop(route, station, (uint)(i + 1),
          (uint)(departureTime.ElementAt(i) - waitMinutes[station]).TotalMinutes,
          (uint)departureTime.ElementAt(i).TotalMinutes);
        trainBaseConnector.AddRouteStop(stop);
      }
      return true;
    }



    #endregion
  }
}
